package net.jalbum.filters;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.util.Map;

import javax.swing.ImageIcon;

import net.jalbum.filterManager.FilterProperties.FilterCategory;

import se.datadosen.jalbum.ModifiesSize;
import se.datadosen.jalbum.Msg;

public class CroppingFilterPlugin extends BoxPanel implements ModifiesSize, MouseMotionListener {
	
	private static final long serialVersionUID = -6690447553047905588L;
        //
        private static double lastSetRatio = 0;
        private double currentRatio = 0;
        private int paintCounter = 0;
        
        private double lastX1 = 0;
        private double lastY1 = 0;
        private double lastX2 = 0;
        private double lastY2 = 0;
        private double lastWidth = 0;
        private double lastHeight = 0;
        
        public Boolean croppingIsLandscape;
        public Boolean imageIsLandscape;
        
        private static final double FLIPBUFFER = 0.1;
        private boolean cropped = false;
        
        CroppingControl croppingControl;

	public CroppingFilterPlugin() {
            
		this.name = Msg.getString(this, "filters.cropping.name");
		this.shortName = Msg.getString(this, "filters.cropping.shortName");
		this.icon = new ImageIcon(this.getClass().getResource("res/crop.png"));
		this.author = "David Fichtmueller (modified by Sascha Rasler)";
		this.version = "1.1";
		this.description = Msg.getString(this, "filters.cropping.description");
		this.category = FilterCategory.BASIC;
		this.prescale = true;
		this.postscale = false;

		// set initial values
                this.X1 = 0.05;
		this.X2 = 0.95;
		this.Y1 = 0.05;
		this.Y2 = 0.95;
                
		this.resizableBox.setInitial(false);
		this.resizableBox.update(this.X1, this.Y1, this.X2, this.Y2);
		this.resizableBox.adjustKeepRatio(CroppingControl.ratioLocked);
		this.croppingControl = new CroppingControl(this);
		this.boxControl = this.croppingControl;
		this.resizableObjectControl = this.croppingControl;
		this.croppingControl.update(this.X1, this.Y1, this.X2 - this.X1, this.Y2 - this.Y1);
	}
        
        @Override
        public void mouseDragged(MouseEvent event) {
            
            super.mouseDragged(event);
            if (croppingControl.keepRatio.isSelected()) {
                double x0 = 0;
                double y0 = 0;
                switch (this.resizableBox.position) {
                    //case 1 = dragging the box
                    case 2: // upperleft
                        x0 = this.resizableBox.X2 * this.resizableBox.width;
                        y0 = this.resizableBox.Y2 * this.resizableBox.height;
                        break;
                    case 3: // upperright
                        x0 = this.resizableBox.X1 * this.resizableBox.width;
                        y0 = this.resizableBox.Y2 * this.resizableBox.height;
                        break;
                    case 4: //lowerright
                        x0 = this.resizableBox.X1 * this.resizableBox.width;
                        y0 = this.resizableBox.Y1 * this.resizableBox.height;
                        break;
                    case 5: //lowerleft
                        x0 = this.resizableBox.X2 * this.resizableBox.width;
                        y0 = this.resizableBox.Y1 * this.resizableBox.height;
                        break;
                }
                double distanceX = Math.abs(event.getX() - x0);
                double distanceY = Math.abs(event.getY() - y0);

                if (this.resizableBox.position > 1) {
                    if (distanceX / distanceY < (1 - FLIPBUFFER)) {
                        if (this.croppingIsLandscape) {
                            this.croppingControl.flipRatio("portrait", this.resizableBox.position);
                        }
                    } else if (distanceX / distanceY > (1 + FLIPBUFFER)) {
                        if (!this.croppingIsLandscape) {
                            this.croppingControl.flipRatio("landscape", this.resizableBox.position);
                        }
                    }
                }
            }
        }
        
        public void determineImageOrientation(){
                    this.imageIsLandscape = ((double)this.width/(double)this.height) > 1;
        }
        
        public void calcNewSize(double desiredRatio, boolean ownCoordinates, boolean selectedFromUI){
                    double boxWidth;
                    double boxHeight;
                    double imageRatio = (double)this.width/(double)this.height;
                    //System.out.println("setting croppingTool's orientation -> is landscape?: " + this.croppingIsLandscape);
                    if(selectedFromUI){
                        determineImageOrientation();
                        if(imageIsLandscape){
                            if(desiredRatio<1){
                                desiredRatio = 1/desiredRatio;
                            }
                        } else {
                            if(desiredRatio>1){
                                desiredRatio = 1/desiredRatio;
                            }
                        }
                    }
                    this.croppingIsLandscape = desiredRatio>1;
                    if (!ownCoordinates){
                        // appyling last known ratio to an untouched / uncropped image
                        // apply as to as large area as possible
                        if(imageRatio < desiredRatio){
                                boxWidth=CroppingControl.CROPSCALE;
                                boxHeight=(imageRatio/desiredRatio)*CroppingControl.CROPSCALE;
                        }else{
                                boxHeight=CroppingControl.CROPSCALE;
                                boxWidth=(desiredRatio/imageRatio)*CroppingControl.CROPSCALE;
                        }
                        this.X1 = 0.5 - boxWidth/2;
                        this.Y1 = 0.5 - boxHeight/2;
                        this.X2 = this.X1 + boxWidth;
                        this.Y2 = this.Y1 + boxHeight;
                        this.resizableBox.setRatio(boxWidth/boxHeight);
                    } else {
                        // ratio and coordinates have already been set once before in this image / i.e. already cropped before
                        this.X1 = lastX1;
                        this.Y1 = lastY1;
                        this.X2 = lastX2;
                        this.Y2 = lastY2;
                        this.resizableBox.setRatio((this.X2-this.X1)/(this.Y2-this.Y1));
                    }
                    this.resizableBox.update(this.X1, this.Y1, this.X2, this.Y2);
                    this.croppingControl.update(this.X1, this.Y1, this.X2 - this.X1, this.Y2 - this.Y1);
        }
        
        public void adjustCurrentCropOrientation(){
            //System.out.println("crop is: " + lastSetRatio);
            this.croppingIsLandscape = lastSetRatio>1;
        }
        
	@Override
	public void setPreviewImage(final BufferedImage bi) {
                
                paintCounter = 0;
                super.setPreviewImage(bi);
		this.croppingControl.initiatePredefinedRatios();
                //setting flag? initial state has been changed...
                this.resizableBox.setInitial(false);
                determineImageOrientation();
                
                if(currentRatio!=0){
                    calcNewSize(currentRatio,true,false);
                    croppingControl.checkExistingRatios(currentRatio);
                }else if (lastSetRatio != 0) {
                    calcNewSize(lastSetRatio,false,false);
                    croppingControl.checkExistingRatios(lastSetRatio);
                } else {
                    // initial setting
                    this.croppingIsLandscape = this.imageIsLandscape;
                    //TODO: set comboBox to image-ratio
                }
		this.renderPreview();
	}
        
	@Override
	public BufferedImage renderImage(final BufferedImage bi) {
                cropped = true;
		return bi.getSubimage((int) (this.X1 * bi.getWidth()), (int) (this.Y1 * bi.getHeight()), (int) ((this.X2 - this.X1) * bi.getWidth()), (int) ((this.Y2 - this.Y1) * bi.getHeight()));
        }

	@Override
	public void renderPreview() {
		this.outputImage = this.filterManager.cloneBufferedImage(this.inputImage);
		this.paint(this.outputImage.getGraphics());
		this.filterManager.setGUIImage(this.outputImage);
	}

	@Override
	public void paint(final Graphics g) {
                // frame color now black - old version : (255, 255, 255, 127):
                g.setColor(new Color(0, 0, 0, 127));
                ++paintCounter;

		final int intX1 = (int) (this.resizableBox.getOutX1() * (this.width - 1));
		final int intX2 = (int) (this.resizableBox.getOutX2() * (this.width - 1));
		final int intY1 = (int) (this.resizableBox.getOutY1() * (this.height - 1));
		final int intY2 = (int) (this.resizableBox.getOutY2() * (this.height - 1));
                
                lastWidth = (this.resizableBox.getOutX2()-this.resizableBox.getOutX1()) * this.width;
                lastHeight = (this.resizableBox.getOutY2()-this.resizableBox.getOutY1()) * this.height;
                
                if (paintCounter != 1) {
                    // setting last set static ratio:
                    lastSetRatio = lastWidth / lastHeight;
                }
                if (paintCounter == 1 && cropped) {
                    // getting this images initial ratio and coordinates:
                    this.currentRatio = lastWidth / lastHeight;
                    lastX1 = this.resizableBox.getOutX1();
                    lastY1 = this.resizableBox.getOutY1();
                    lastX2 = this.resizableBox.getOutX2();
                    lastY2 = this.resizableBox.getOutY2();
                }
                
		// draw the semi-transparent panels around the selected area of the image
		// upper left panel
		if (intX2 + 1 > 0 && intY1 > 0) {
			g.fillRect(0, 0, intX2 + 1, intY1);
		}
		// upper right panel
		if (this.resizableBox.getWidth() - 1 - intX2 > 0 && intY2 + 1 > 0) {
			g.fillRect(intX2 + 1, 0, this.resizableBox.getWidth() - 1 - intX2, intY2 + 1);
		}
		// lower right panel
		if (this.resizableBox.getWidth() - intX1 > 0 && this.resizableBox.getHeight() - 1 - intY2 > 0) {
			g.fillRect(intX1, intY2 + 1, this.resizableBox.getWidth() - intX1, this.resizableBox.getHeight() - 1 - intY2);
		}
		// lower left panel
		if (intX1 > 0 && this.resizableBox.getHeight() - intY1 > 0) {
			g.fillRect(0, intY1, intX1, this.resizableBox.getHeight() - intY1);
		}
		// draw the handles if the mouse of over or near the box
		this.resizableBox.paintHandles(g);
	}

	public Dimension getModifiedSize(final Dimension dimension, final Map arg1) {
		return new Dimension((int) ((this.X2 - this.X1) * dimension.getWidth()), (int) ((this.Y2 - this.Y1) * dimension.getHeight()));
	}
}
